#include "bacterialcontigbrowser.h"
//#include "ui_bacterialcontigbrowser.h"

//GLOBAL
//SORT FUNCTIONS
bool contigLengthLessThan(const QPointer<Contig> &c1, const QPointer<Contig> &c2)
{
    return c1->getNTInContig() > c2->getNTInContig();
}

bool contigContainsSelectedGenesLessThan( const QPointer<Contig> &c1, const QPointer<Contig> &c2)
{
    //inital version- will just examine sorting one on selected cluster
    bool c1HasCluster = c1->containsCluster( selectedClusters[0] );
    bool c2HasCluster = c2->containsCluster( selectedClusters[0] );

    return c1HasCluster;
    //return c1->getNTInContig() > c2->getNTInContig();
}

////////////////////////////////////////////////////////////////////////////////////

//default constructor- give a graphics item parent
BacterialContigBrowser::BacterialContigBrowser(SageVis *svp, QGraphicsItem *parent) :
    VisBaseWidget(svp, parent)
{
    //variables to control the view -- note -right now must be set within loadData not here.  Don't know why
    startPosOfContigs = boundingRect().y();
    deltaY = 0.0;
    minContigHeight = 6.0;
    maxContigHeight = 50.0;

    rubberBandOrigin = new QPointF;

    setCursor( Qt::ArrowCursor );//make sure arrow cursor is shown in general
    setFlag( QGraphicsItem::ItemClipsChildrenToShape );//contigs stay in bounds
    setFlag( QGraphicsItem::ItemIsFocusable );//needed for keypress event

    init = false; //haven't initialized the data yet

    backgroundColor = new QColor( 25, 25, 25, 255);
    //setCacheMode(QGraphicsItem::ItemCoordinateCache);//this is not faster- slower, perhaps due to memory demands

    highlightRect.setCoords(0,0,0,0);

    gffFileName = new QString;
    fastaFileName = new QString;

    menuBar = new ContigBrowserMenuBar( this );
    menuBar->setWidth( boundingRect().width()*.1);
    menuBar->setHeight( boundingRect().height() );
    menuBar->setPos(boundingRect().x(), boundingRect().y());
    menuBar->setZValue(100.0);//draw above the bacterial contig browser

//    menuBar->setVisible(false);
//    menuBar->setWidth(0);
}

BacterialContigBrowser::~BacterialContigBrowser()
{
    if( backgroundColor )
        delete backgroundColor;
    qDeleteAll(contigs);

}

bool BacterialContigBrowser::loadData(QString pathToFile) //not using this anymore
{
    loadData2(pathToFile);
}

bool BacterialContigBrowser::load( QString gffFile, QString fastaFile )
{
    QThread* thread = new QThread;
    GenomeDataUploader* dataUploader = new GenomeDataUploader(gffFile, fastaFile);
    dataUploader->moveToThread(thread);
    connect(dataUploader, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
    connect(thread, SIGNAL(started()), dataUploader, SLOT(process()));
    connect(dataUploader, SIGNAL(finished()), thread, SLOT(quit()));
    connect(dataUploader, SIGNAL(finished()), dataUploader, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(dataUploader, SIGNAL(trackProgress(int)), this, SLOT(updateProgressBar(int)));
    connect(dataUploader, SIGNAL(createdReformattedFileSignal(QString)), this, SLOT(createdReformattedFileSlot(QString)));
    connect(dataUploader, SIGNAL(finished()), this, SLOT(createContigsSlot()));
    thread->start();

    menuBar->updateProgressMessage("Uploading data and running CdHit");

}

void BacterialContigBrowser::createdReformattedFileSlot(QString rf)
{
    reformattedFile.append(rf);
}

void BacterialContigBrowser::createContigsSlot()
{
//    QThread* thread = new QThread;
//    GenomeFactory* factory = new GenomeFactory(reformattedFile, &contigs, this);
//    factory->moveToThread(thread);
//    connect(factory, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
//    connect(thread, SIGNAL(started()), factory, SLOT(process()));
//    connect(factory, SIGNAL(finished()), thread, SLOT(quit()));
//    connect(factory, SIGNAL(finished()), factory, SLOT(deleteLater()));
//    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
//    connect(factory, SIGNAL(trackProgress(int)), this, SLOT(updateProgressBar(int)));
//    connect(factory, SIGNAL(gotContigs(QList<QPointer<Contig> >*)), this, SLOT(layoutContigsSlot(QList<QPointer<Contig> >*)));
//    thread->start();

    menuBar->updateProgressMessage("Creating Contigs and Genes");
    menuBar->updateProgressBar(10);
    loadData2(reformattedFile);
}

void BacterialContigBrowser::layoutContigsSlot(QList<QPointer<Contig> >* c)
{

    //FIRST SET UP DIMENSIONS and GENOME SEGMENT POSITION
    //determine defaultContigHeight based on size of window and number of contigs to display
    defaultContigHeight = boundingRect().height() / (float)contigs.size();
    if( defaultContigHeight > maxContigHeight )
        defaultContigHeight = maxContigHeight;
    else if( defaultContigHeight < minContigHeight )
        defaultContigHeight = minContigHeight;

    //give all the contigs this height
    for(int i = 0; i < contigs.size(); i++)
    {
        contigs[i]->setHeight( defaultContigHeight );
    }

    //set contig width -- note: contigs ALL go to end of the space.  Genome segments inside the contigs
        //get variable widths depending on the nt/pixel scale and the num of nt inside contig
    float xStart = boundingRect().x()+boundingRect().width()*.12; //at edge, shifted slightly
    float xFinish = boundingRect().y() + boundingRect().width();//go to end of space
    for(int i = 0 ; i < contigs.size(); i++ )
    {
        contigs[i]->setWidth( xFinish-xStart );
    }

    //setup text space- so all genome segments will be in line
    maxTextWidth = 50;//need to know this so can compute nt/pixel based on maxNT and pixel width of non-text space
    for(int i = 0; i < contigs.size(); i++)
    {
        int thisTextWidth = contigs[i]->getMaxTextWidth( );
        if( thisTextWidth > maxTextWidth )
            maxTextWidth = thisTextWidth;
    }

    //set this text width accross all contigs
    for(int i = 0; i < contigs.size(); i++)
    {
        contigs[i]->setTextWidth( maxTextWidth*1.15 );
    }

    //setup genomeSegmentWidth
    //  it is proportional to its size in nt and longest contig --NOTE- 50 will be changed later to width of text
    for( int i = 0; i < contigs.size(); i++ )
    {
        contigs[i]->setGenomeSegmentWidth( ((float)xFinish-xStart-maxTextWidth-10.0)*((float)contigs[i]->getNTInContig()
                                                                             / (float)maxNTPerContig) );
    }

    //set start contigs to be at the top of the window -- this will change as user navigates
    startContig = 0;
    startPosOfContigs = boundingRect().y();
    positionContigs(); //set contig positions

    //Layout genes in contig
    for(int i = 0 ; i < contigs.size(); i++)
    {
        contigs[i]->setVisible( true );

        contigs[i]->layoutGenes();

        connect(this, SIGNAL(timeout()), contigs[i], SLOT(advance()));
    }

    connectContigs();

}


//NOT USING THIS NOW
bool BacterialContigBrowser::loadData(QString gffFile, QString fastaFile)
{
    bool worked = false;

    load(gffFile, fastaFile);

    return true;

    ////////////////////////////////////////////////////////////////////////////

#if defined( Q_WS_WIN)

    //these file names get saved - I could write the following more efficiently,
    //but I don't want to rewrite now....
    gffFileName->append(gffFile);//pathToFile);
    fastaFileName->append(fastaFile); //"C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\1201_homolog_list.fa");

//    //FIRST read fasta and get gene clusters
    QString inputFastaFile = fastaFile; //"C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\1201_homolog_list.fa";
    QString outputClusterFile = "C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\cdHitOut";
    readFastaOutputCdHitCluster( inputFastaFile, outputClusterFile );

    //THEN read gff file to create datasets
    QString gffInputFile = gffFile;//"C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\1201_seq.gff2";
    QString inputClusterFile = outputClusterFile.append(".clstr");
    QString temp = gffInputFile;
    QString outputTabDelimFile = temp.remove("gff2").append("reformat.tab");
    readClusterAndGFFWriteTabDelim(inputClusterFile, gffInputFile, outputTabDelimFile);

    //finally, read tab delim and process
    loadData2( outputTabDelimFile );

#elif defined( Q_OS_LINUX )
    //gffFileName->append(pathToFile);
    //fastaFileName->append( "C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\1201_homolog_list.fa");

    //FIRST read fasta and get gene clusters
    QString inputFastaFile = fastaFile; //"C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\1201_homolog_list.fa";
    QString outputClusterFile = "C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\cdHitOut";
    readFastaOutputCdHitCluster( inputFastaFile, outputClusterFile );

    //THEN read gff file to create datasets
    QString gffInputFile = gffFile;//"C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\data\\contigs\\1201_seq.gff2";
    QString inputClusterFile = outputClusterFile.append(".clstr");
    QString temp = gffInputFile;
    QString outputTabDelimFile = temp.remove("gff2").append("reformat.tab");
    readClusterAndGFFWriteTabDelim(inputClusterFile, gffInputFile, outputTabDelimFile);

    //finally, read tab delim and process
    loadData2( outputTabDelimFile );
#else
    loadData2( pathToFile );
#endif

}

bool BacterialContigBrowser::readFastaOutputCdHitCluster(QString inputFastaFile, QString outputClusterFile )
{
    qDebug() << "testing QProcess " << endl;
    QString program = "C:\\Documents and Settings\\jaurisa\\My Documents\\Development\\sagevis\\sageVis-restructured\\SageVis\\programs\\cd-hit-win\\cd_hit";
    QStringList arguments;
    arguments << "-i" << inputFastaFile << "-o" << outputClusterFile <<
                 "-c" << ".9" << "-n" << "5" << "-d" << "1000";

    cdHit = new QProcess(this);
    cdHit->setProcessChannelMode(QProcess::MergedChannels);
    cdHit->start(program, arguments);
    qDebug() << "cdhit started " << endl;

    // Wait for it to start
    if(!cdHit->waitForStarted())
    {
        qDebug() << "not waiting for started " << endl;
        return false; //note - not handling false return right now...
    }

    // Continue reading the data until EOF reached
    QByteArray data;

    while(cdHit->waitForReadyRead())
    {
        data.append(cdHit->readAll());
        qDebug() << "cdhit running " << endl;

    }

    // Output the data
    qDebug(data.data());
    qDebug("Done!");

    if( cdHit->error() )
        return false;

    return cdHit->exitStatus() == QProcess::NormalExit;
}

bool BacterialContigBrowser::readClusterAndGFFWriteTabDelim(QString clusterInputFile, QString gffInputFile, QString outputTabDelimFile)
{

    //note- later can launch cd_hit on faa file to create cluster file.  In this case, don't use clusterInputFile
    //but then can't expect to get all the annotations i am currently assuming ... =/

    QHash<QString, QString> geneIdToClusterHT; // this will store the cluster id for all the genes

    //fill gene id to cluster ht
    QFile cluster( clusterInputFile );
    if( !cluster.exists() )
    {
        qDebug() << "cluster file not found." << endl;
        return false;
    }
    if( ! cluster.open(QIODevice::ReadOnly))
    {
        qDebug() << "file " << clusterInputFile << " can't be opened." << endl;
        return false;
    }
    qDebug() << "file " << gffInputFile << " opened." << endl;

    QTextStream clusterStream(&cluster);

    QString clusterId = ".";
    QString geneId;
    QString line;
    do {
        line = clusterStream.readLine(); //read each line
        //qDebug() << "line = " << line << endl;

        if( line.contains("Cluster") ) //grab cluster ids
        {
            clusterId = line.remove(">").remove(" ");

            //qDebug() << "cluster id = " << clusterId << endl;
        }
        else //grab gene id
        {
            QRegExp rx(">.*.pep");//grabs the >geneId.pep
            int pos = rx.indexIn( line );
            QStringList list = rx.capturedTexts();
            for(int i = 0; i < list.size(); i++)
            {
                geneId = list[i].remove(".pep").remove(">");//can also in case remove .cdna  or .cds or what have you
                //qDebug() << "------ geneId    " << geneId << "   ----  cluster id     " << clusterId << endl;

                geneIdToClusterHT.insert(geneId, clusterId); //inster in hashtable
            }
        }
    }while( !clusterStream.atEnd());

    cluster.close();

    //now read gff2 file and output the reformated file

    QFile gff2(gffInputFile);

    QFile out( outputTabDelimFile );

    if( !gff2.exists() )
    {
        qDebug() << "file not found." << endl;
        return false;
    }

    if( ! gff2.open(QIODevice::ReadOnly))
    {
        qDebug() << "file " << gffInputFile << " can't be opened." << endl;
        return false;
    }

    if( ! out.open( QIODevice::WriteOnly) )//
    {
        qDebug() << "failed to open output file " << endl;
        return false;
    }

    qDebug() << "file " << gffInputFile << " opened." << endl;

    //this is for csv files... works for now
    QTextStream inStream(&gff2);
    QTextStream outStream(&out);

    int mRNACount = 0;
    int infoCount = 0;
    clusterId = "no cluster";
    do {
        line = inStream.readLine();
        //qDebug() << "line = " << line << endl;

        if( ! line.startsWith("#") )
        {
            //divide line into major tokens- by the tab delim
           QStringList majorTokens = line.split("\t", QString::SkipEmptyParts);

           //get species, strain and contig from the first token
           //note- may not be able to always assume that format is species_strain_contig
           QStringList speciesStrainContig = majorTokens[0].split( "_", QString::SkipEmptyParts );
           QString species = speciesStrainContig[0];
           QString strain = speciesStrainContig[1];
           QString contig =  speciesStrainContig[2];

           //qDebug() << "    species = " << species << "   strain = " << strain << "   contig = " << contig << endl;

           int start = majorTokens[3].toInt();//start is forth token
           int stop = majorTokens[4].toInt();//stop if fifth
           QString strand = majorTokens[6]; //true;
           //if( majorTokens[6].contains("-"))//strand is sixth
               //posStrand = false;

           //NOTE:  only considering mRNA's- ignoring operons for now
           if( majorTokens[2].contains("mRNA") )
           {
               mRNACount++;
               QString id = ".";//all should have an id:  Species_Strain_Contig_G_N.N
               QString uniprot = "-";//not all will have uniprot id
               QString description = "-";//not all will have a description
               QString name = "-";
               QString perc_qry_cvrg = "-";
               QString perc_hit_cvrg = "-";
               QString perc_ident = "-";
               QString exp = "-";

               //get id
               if( majorTokens[8].contains("ID=") )
               {
                   QRegExp rx("ID=[^;]*;");//ID=any num characters execpt ; then ;
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       id = list[i].remove(".cdna;").remove("ID=");
                       //qDebug() << "------ id    " << id << endl;
                   }
               }

               //get uniprot
               if( majorTokens[8].contains("Note=") )
               {
                   QRegExp rx("Note=[^:]*:");//Note=any num characters execpt : then :
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       uniprot = list[i].remove("Note=").remove(":");
                       //qDebug() << "------ uniprot    " << uniprot << endl;
                   }
               }

               //get description
               if( majorTokens[8].contains("Note=") )
               {
                   //QRegExp rx("Note=[^=]*n=");//Note=any num characters then n=
                   QRegExp rx("Note=[^%=]*[%=]");//Note=any num characters then n=
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {

                       //QRegExp rx2(":[^=]*n=");//take first regexp, apply new pattern  : any num char n=
                       QRegExp rx2(":[^%=]*[%=]");//take first regexp, apply new pattern  : any num char n=
                       int pos2 = rx2.indexIn( list[i]);
                       QStringList list2 = rx2.capturedTexts();
                       description = list2[0].remove(":").remove("n=").remove("(/%");
                       //qDebug() << "------ getting descr    " << description << endl;
                   }
               }

               //get name
               if( majorTokens[8].contains("RepID=") )
               {
                   QRegExp rx("RepID=[^(]*");//Note=any num characters then n=
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       name = list[i].remove("RepID=").remove(" ");//.remove("(");
                       //qDebug() << "------name    " << name << endl;
                   }
               }

               //get perc_qry_cvrg
               if( majorTokens[8].contains("/%qry_cvrg=") )
               {
                   QRegExp rx("/%qry_cvrg=[0-9]{1,3}");//Note=any num characters then n=
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       //perc_qry_cvrg = (list[i].remove("/%qry_cvrg=")).toInt();//.remove("(");
                       perc_qry_cvrg = (list[i].remove("/%qry_cvrg=").remove(" "));//.toInt();//.remove("(");
                       //qDebug() << "------perc_qry_cvrg    " << perc_qry_cvrg << endl; //<< list[i] << endl;//
                   }
               }

               //get perc_hit_cvrg
               if( majorTokens[8].contains("/%hit_cvrg=") )
               {
                   QRegExp rx("/%hit_cvrg=[0-9]{1,3}");//Note=any num characters then n=
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       //perc_qry_cvrg = (list[i].remove("/%qry_cvrg=")).toInt();//.remove("(");
                       perc_hit_cvrg = (list[i].remove("/%hit_cvrg=").remove(" "));//.toInt();//.remove("(");
                       //qDebug() << "------perc_hit_cvrg    " << perc_hit_cvrg << endl; //<< list[i] << endl;//
                   }
               }

               //get perc_ident
               if( majorTokens[8].contains("/%ident=") )
               {
                   QRegExp rx("/%ident=[0-9]{1,3}");//Note=any num characters then n=
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       //perc_qry_cvrg = (list[i].remove("/%qry_cvrg=")).toInt();//.remove("(");
                       perc_ident = (list[i].remove("/%ident=").remove(" "));//.toInt();//.remove("(");
                       //qDebug() << "------perc_ident    " << perc_ident << endl; //<< list[i] << endl;//
                   }
               }

               //get exp
               if( majorTokens[8].contains("/exp=") )
               {
                   QRegExp rx("/exp=[^//]* ");//Note=any num characters then n=
                   int pos = rx.indexIn( majorTokens[8]);
                   QStringList list = rx.capturedTexts();
                   for(int i = 0; i < list.size(); i++)
                   {
                       exp = (list[i].remove("/exp=").remove(" "));//.toInt();//.remove("(");
                       //qDebug() << "------exp    " << exp << endl; //<< list[i] << endl;//
                   }
               }

               QList<QString> list = geneIdToClusterHT.values( id );
               if( list.size() >= 1 )
                 clusterId = list[0]; //big assumption here that something will be found
               else
                   qDebug() << "LOOKUP FAILED " <<endl;

               //make sure something is output
               if( uniprot.size() < 1 )
                    uniprot = "-";//not all will have uniprot id
               if( description.size() < 1 )
                    description = "-";//not all will have a description
               if( name.size() < 1 )
                    name = "-";
               if( perc_qry_cvrg.size() < 1 )
                    perc_qry_cvrg = "na";
               if( perc_hit_cvrg.size() < 1 )
                    perc_hit_cvrg = "na";
               if( perc_ident.size() < 1 )
                    perc_ident = "na";
               if( exp.size() < 1 )
                    exp = "na";

               outStream << species << "\t" << strain << "\t" << contig << "\t" << id << "\t" << start << "\t" << stop << "\t" << strand
                            << "\t" << clusterId << "\t" << uniprot << "\t" << description << "\t" << name << "\t" << perc_qry_cvrg << "\t" << perc_hit_cvrg
                               << "\t" << perc_ident << "\t" << exp << endl;
               clusterId = "no cluster";

           }//end mRNA

        }//end line

        qDebug() << "mRNA count = " << mRNACount << "  infoCount =  " << infoCount << endl;
    } while (!inStream.atEnd() );

    gff2.close();
    out.close();

    return true;
}


//Revised load data
//note- right now I am reading NOT gff2 but a tab delim file
bool BacterialContigBrowser::loadData2( QString pathToFile )
{
    qDebug() << "in bac contig browser reading file " << pathToFile << endl;

    bool worked;

    //Note- eventually, will need to do this more intelligently
    if( pathToFile.contains("annon") || pathToFile.contains("Annon") )
    {
        worked = readTabDelimFile( pathToFile );
    }
    else
    {
        worked = readReformatedFile( pathToFile );
    }

    if( worked )
    {
        //FIRST SET UP DIMENSIONS and GENOME SEGMENT POSITION
        //determine defaultContigHeight based on size of window and number of contigs to display
        defaultContigHeight = boundingRect().height() / (float)contigs.size();
        if( defaultContigHeight > maxContigHeight )
            defaultContigHeight = maxContigHeight;
        else if( defaultContigHeight < minContigHeight )
            defaultContigHeight = minContigHeight;

        //give all the contigs this height
        for(int i = 0; i < contigs.size(); i++)
        {
            contigs[i]->setHeight( defaultContigHeight );
        }

        menuBar->updateProgressBar(20);

        //set contig width -- note: contigs ALL go to end of the space.  Genome segments inside the contigs
            //get variable widths depending on the nt/pixel scale and the num of nt inside contig
        float xStart = boundingRect().x()+boundingRect().width()*.12; //at edge, shifted slightly
        float xFinish = boundingRect().y() + boundingRect().width();//go to end of space
        for(int i = 0 ; i < contigs.size(); i++ )
        {
            contigs[i]->setWidth( xFinish-xStart );
        }

        menuBar->updateProgressBar(30);


        //setup text space- so all genome segments will be in line
        maxTextWidth = 50;//need to know this so can compute nt/pixel based on maxNT and pixel width of non-text space
        for(int i = 0; i < contigs.size(); i++)
        {
            int thisTextWidth = contigs[i]->getMaxTextWidth( );
            if( thisTextWidth > maxTextWidth )
                maxTextWidth = thisTextWidth;
        }

        //set this text width accross all contigs
        for(int i = 0; i < contigs.size(); i++)
        {
            contigs[i]->setTextWidth( maxTextWidth*1.15 );
        }

        menuBar->updateProgressBar(40);

        //setup genomeSegmentWidth
        //  it is proportional to its size in nt and longest contig --NOTE- 50 will be changed later to width of text
        for( int i = 0; i < contigs.size(); i++ )
        {
            contigs[i]->setGenomeSegmentWidth( ((float)xFinish-xStart-maxTextWidth-10.0)*((float)contigs[i]->getNTInContig()
                                                                                 / (float)maxNTPerContig) );
        }

        //set start contigs to be at the top of the window -- this will change as user navigates
        startContig = 0;
        startPosOfContigs = boundingRect().y();

        menuBar->updateProgressBar(80);

        positionContigs(); //set contig positions

        menuBar->updateProgressBar(100);

        menuBar->updateProgressMessage("Layout elements");

        //Layout genes in contig
        for(int i = 0 ; i < contigs.size(); i++)
        {

            if( ( (i*100) / contigs.size() ) % 10 == 0 )
                updateProgressBar( ((i*100) / contigs.size() ) % 10 ) ;
            contigs[i]->setVisible( true );

            contigs[i]->layoutGenes();

            connect(this, SIGNAL(timeout()), contigs[i], SLOT(advance()));
        }

        connectContigs();

    }//end if worked
    else
    {
        //must say something to user, such as: load data failed, please ensure input is as expected
        menuBar->updateProgressMessage("FAILED!");
    }
}

//this will set contigs to new heights, unless it has the non-default height
//          --- need to think about this more though ...
void BacterialContigBrowser::setContigHeights(float newHeight)
{
//    //give all the contigs this height
//    for(int i = 0; i < contigs.size(); i++)
//    {
//        if( contigs[i]->getHeight() == defaultContigHeight ) //only change height if it is equal to default height
//            contigs[i]->setHeight( newHeight );
//    }
}


//there is a pre-set input startContig --- eg the contig at the top
//and the contigs all have been given heights and widths and all dimensions
//just need to be positioned in Y
void BacterialContigBrowser::positionContigs()
{
    if( contigs.size() == 0 )
        return;
    float xStart = boundingRect().x()+(menuBar->boundingRect().width()*1.05); //at edge, shifted slightly
    float xFinish = boundingRect().y() + boundingRect().width();//go to end of space

    //SET POSITION OF CONTIGS
    //1: Give start contig position -- x is default x pos- end of screen, y is at 0 + 1/2 * height
    contigs[startContig]->setPos( xStart, startPosOfContigs+contigs[startContig]->getHeight()/2.0);

    //2: Move backwards from startContig to 0
    //      note- there is no gap between contigs in this scheme
    //      we subtract half the height because contigs are centered on y
    float currY = startPosOfContigs;
    for(int i = startContig-1; i >= 0; i-- )
    {
        contigs[i]->setPos( xStart, currY - contigs[i]->getHeight()/2.0 );
        currY = currY - contigs[i]->getHeight();
    }

    //3: Move forwards from startContig and set the other positions
    currY = startPosOfContigs+contigs[startContig]->getHeight(); //start at end of first contig
    for( int i = startContig+1; i < contigs.size(); i++ )
    {
        contigs[i]->setPos( xStart, currY + contigs[i]->getHeight()/2.0 );
        currY = currY + contigs[i]->getHeight();
    }
}

//reposition contigs when hovering over a contig
void BacterialContigBrowser::repositionContigsAroundContigN(int contigN)
{
    //int posForContig = contigs[contigN]->boundingRect().y() +
    if( contigs.size() == 0 )
        return;
    float xStart = boundingRect().x()+(menuBar->boundingRect().width()*1.05); //at edge, shifted slightly
    float xFinish = boundingRect().y() + boundingRect().width();//go to end of space

    //SET POSITION OF CONTIGS
    //1: Give start contig position -- x is default x pos- end of screen, y is at 0 + 1/2 * height
    contigs[contigN]->setPos( xStart, startPosOfContigs+contigs[startContig]->getHeight()/2.0);

    //2: Move backwards from startContig to 0
    //      note- there is no gap between contigs in this scheme
    //      we subtract half the height because contigs are centered on y
    float currY = startPosOfContigs;
    for(int i = startContig-1; i >= 0; i-- )
    {
        contigs[i]->setPos( xStart, currY - contigs[i]->getHeight()/2.0 );
        currY = currY - contigs[i]->getHeight();
    }

    //3: Move forwards from startContig and set the other positions
    currY = startPosOfContigs+contigs[startContig]->getHeight(); //start at end of first contig
    for( int i = startContig+1; i < contigs.size(); i++ )
    {
        contigs[i]->setPos( xStart, currY + contigs[i]->getHeight()/2.0 );
        currY = currY + contigs[i]->getHeight();
    }

}

void BacterialContigBrowser::connectContigs()
{
    for(int i = 0 ; i < contigs.size(); i++ )
    {
        for(int j = i; j < contigs.size(); j++)
        {
            contigs[i]->forgeConnection( contigs[j] );
        }
    }
}

//This function reads in a specialized data file of the format:
//    species \t contigId \t geneId \t start \t stop \t strand \t cluster \t uniprotId \t description
//        \t name \t perc_qry_cvrg \t perc_hit_cvrg \t perc_ident \t exp
//- there are multiple genes per contig- so contigs ids will be on multiple lines
//- genes arise in multiple contigs- this should be
//- start is always before stop- so this is not an indicator of true gene start site and stop site
//    that can found in the strand.  IF pos strand, start is the start site (I think ... ? )
bool BacterialContigBrowser::readReformatedFile( QString pathToFile )
{
    QFile file(pathToFile);

    if( !file.exists() )//make sure file exists
    {
        qDebug() << "file not found." << endl;
        return false;
    }

    if( ! file.open(QIODevice::ReadOnly)) //and can be openned
    {
        qDebug() << "file " << pathToFile << " can't be opened." << endl;
        return false;
    }

    qDebug() << "file " << pathToFile << " opened." << endl;

    QTextStream stream(&file);//text stream to stream data in from file

    QHash< QString, int > contigHash;//this allows me to keep track of whether contig listed on a line has already been encountered
    int contigCount = 0;

    QHash< QString, int > geneHash; //same as for contigs- except I need this to generate a complete gene list
    int geneCount;

    maxNTPerContig = 0;

    QString line;//get each line
    do {
         line = stream.readLine();
         //qDebug() << line << endl;

        //Note: for now I am assuming that each contig is in a sep strain- but this may not always be true
         //will have to adapt

         //first get the contig idx
         int contigIdx = -1;
         QStringList list = line.split("\t");//tab delimited- so returns the contig, gene, start, stop, strand
         QString contigId = list[0].append( "_").append(list[1]).append("_").append( list[2] );
         if( contigHash.contains( contigId )) //if already encountered this contig
         {
             contigIdx = contigHash.value(contigId);//get the idx
         }
         else //not encountered
         {
             contigIdx = contigCount;
             contigCount++;//counter hold the number of contigs that have been added thus far
             contigs << new Contig( contigId, this );//list[0], this ); //create contig
             contigHash.insert( contigId, contigIdx );//list[0], contigIdx);//add to hash table
         }

         QString geneId = list[7]; //now getting geneId from 7 - which is really the cluster id.  Will change this later
         int start = list[4].toInt();
         int stop = list[5].toInt();
         bool posStrand = list[6].contains("+") ? true : false;

         contigs[ contigIdx ]->addGene( geneId, start, stop, posStrand, line, "reformatted" );

         if( stop > maxNTPerContig )
             maxNTPerContig = stop;

    } while (!stream.atEnd() );//&& contigCount <= maxContigsDisplayed );
    //note- currently loading only the data that is displayed.  Eventually this means I will have to implement caching

    file.close();

    return true;

}


//This function reads in a specialized data file of the format:
//   contigId  geneId   start  stop   strand
//- there are multiple genes per contig- so contigs ids will be on multiple lines
//- genes arise in multiple contigs
//- start is always before stop- so this is not an indicator of true gene start site and stop site
//    that can found in the strand.  IF pos strand, start is the start site (I think ... ? )
bool BacterialContigBrowser::readTabDelimFile( QString pathToFile )
{
    QFile file(pathToFile);

    if( !file.exists() )//make sure file exists
    {
        qDebug() << "file not found." << endl;
        return false;
    }

    if( ! file.open(QIODevice::ReadOnly)) //and can be openned
    {
        qDebug() << "file " << pathToFile << " can't be opened." << endl;
        return false;
    }

    qDebug() << "file " << pathToFile << " opened." << endl;

    QTextStream stream(&file);//text stream to stream data in from file

    QHash< QString, int > contigHash;//this allows me to keep track of whether contig listed on a line has already been encountered
    int contigCount = 0;

    QHash< QString, int > geneHash; //same as for contigs- except I need this to generate a complete gene list
    int geneCount;

    maxNTPerContig = 0;

    QString line;//get each line
    do {
         line = stream.readLine();
         //qDebug() << line << endl;

        //Note: for now I am assuming that each contig is in a sep species- but this may not always be true
         //will have to adapt

         //first get the contig idx
         int contigIdx = -1;
         QStringList list = line.split("\t");//tab delimited- so returns the contig, gene, start, stop, strand
         if( contigHash.contains( list[0] )) //if already encountered this contig
         {
             contigIdx = contigHash.value(list[0]);//get the idx
         }
         else //not encountered
         {
             contigIdx = contigCount;
             contigCount++;//counter hold the number of contigs that have been added thus far
             contigs << new Contig( list[0], this ); //create contig
             contigHash.insert( list[0], contigIdx);//add to hash table
         }

         QString geneId = list[1];
         int start = list[2].toInt();
         int stop = list[3].toInt();
         bool posStrand = list[3].contains("+") ? true : false;
         contigs[ contigIdx ]->addGene( geneId, start, stop, posStrand );

         if( stop > maxNTPerContig )
             maxNTPerContig = stop;

    } while (!stream.atEnd() );

    file.close();

    return true;
}

//-----------------------DRAWING FUNCTIONS------------------------------------
////NOTE- I am using the inherited boundingRect function
//QRectF BacterialContigBrowser::boundingRect() const
//{
//    QRectF r ( -600, -400, 1200, 800 );//-400, -375, 1050, 750);

////#if defined(Q_OS_LINUX)
////    r( -1360.0*4.5/2.0, -768.0*2.5/2.0, -1360.0*5.5/2.0, -768.0*2.5 );
////#endif

//    return r;
//}

QPainterPath BacterialContigBrowser::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}


void BacterialContigBrowser::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->setBrush(*backgroundColor);

    QRectF bRect = boundingRect();
    //bRect.adjust(1, 1, -1, -1);
    painter->drawRect(bRect);

    QColor highlightColor( backgroundColor->red()+10.0, backgroundColor->green()+10.0, backgroundColor->blue()+10.0);
    painter->setBrush( highlightColor );
    painter->setPen( Qt::NoPen );
    painter->drawRect(highlightRect);
}


//-----------------------END DRAWING FUNCTIONS------------------------------------

//-----------------------EVENT SLOTS------------------------------------

void BacterialContigBrowser::keyPressEvent ( QKeyEvent * event )
{
    qDebug() << event->text() << endl;

    if( event->text().compare("s") == 0 )
    {
        qSort( contigs.begin(), contigs.end(), contigLengthLessThan );
        positionContigs();
        update();
    }

    if( event->modifiers() == Qt::ControlModifier )
    {
        //Launch of new file
        if( event->key() == 78 || event->key() == 110  // N or n
           || event->key() == 70 || event->key() == 102 ) // F or f
        {
//            qDebug() << "in bac contig browser, control f or n" << endl;

//            qDeleteAll(contigs);//delete the contig objects
//            contigs.clear();//clear the list

//            qDebug() << contigs.size() << endl;

//            update();

//            QString pathToData = "../../../../SageVis/data";
//            #if defined(  Q_WS_WIN )
//                pathToData = "./data";
//            #endif
//            #if defined(  Q_OS_LINUX )
//                pathToData = "./data";
//            #endif

//           QFileDialog *fileDialog = new QFileDialog;
//           fileDialog->setVisible(true);

//           QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, Qt::Window);
//           proxy->setWidget(fileDialog);

//           QString fileName = fileDialog->getOpenFileName();
//           qDebug() << "fileName" << fileName << endl;

////           QString fileName = fileDialog.getOpenFileName((QGraphicsWidget)this,
////                     tr("Open Image"), QDir::currentPath(), tr("Image Files (*.png *.jpg *.bmp)"));

//           loadData( fileName );
////            #if defined( Q_WS_WIN )
////                loadData( pathToData.append( "/contigs/1201_seq.reformat.tab"));///contigs/1201_seq_sorted_flipped.reformat.tab"));//1201_seq_sorted.reformat.tab"));//"/contigs/1201_seq.reformat.tab"));//Cry3_contigs.reformat.tab" ) );//using relative path now
////            #endif
////            #if defined( Q_OS_LINUX )
////                loadData(  pathToData.append( "/contigs/annonContigData.txt" ) );//using relative path now
////            #endif
////            #if defined( Q_WS_MAC  )
////                loadData(pathToData.append( "/contigs/annonContigData.txt" ) );//using relative path now
////            #endif

////            update();

//           delete proxy;
           //delete fileDialog; //If i delete both, program crashes.  So deleting proxy may be sufficient ... ?

        }
        //Sort- in case someone forgets they can do it with just 's'
        if( event->key() == 83 || event->key() == 115 )
        {
            qSort( contigs.begin(), contigs.end(), contigLengthLessThan );
            positionContigs();
            update();
        }
    }
}



//wheel event will control density of contig display
//by resetting default contig height
//      note- if contigs have variable heights, will need to restructure
//      now i am assuming they all have the default height
void BacterialContigBrowser::wheelEvent(QGraphicsSceneWheelEvent *event)
{
    //look at scroll event
    int numDegrees = event->delta() / 8;
    float numSteps = numDegrees / 30.0;//15.0; //not ideal value yet .. still a bit choppy.

    //zoom in, contig height increases
    if(numDegrees > 0 )
    {
        defaultContigHeight+=numSteps;
        if( defaultContigHeight > maxContigHeight )
            defaultContigHeight = maxContigHeight;
    }
    else //zoom out, more contigs displayed
    {
        defaultContigHeight+=numSteps;
        if( defaultContigHeight < minContigHeight )
            defaultContigHeight = minContigHeight;
    }

    for(int i = 0; i < contigs.size(); i++)
    {
        contigs[i]->setHeight(defaultContigHeight);
        contigs[i]->layoutGenes();
    }

    qDebug() << numSteps << " " << numDegrees << " " << defaultContigHeight << endl;

    positionContigs();

    update();
}

//on mouse press, user is initializing a 'click and drag navigation event'
// set 'clickAndDragInitialized' to be true, and set up the mousePressStartPoint as well as deltaY
// so that the click and drag event can be processed
void BacterialContigBrowser::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "mouse press " << event->pos().x() << " " << event->pos().y()  ;

    if( event->modifiers() == Qt::ControlModifier )
    {
        qDebug() << "creating rubber band at " << event->pos().x() << "," << event->pos().y() << endl;
        rubberBandOrigin->setX( event->pos().x() );
        rubberBandOrigin->setY( event->pos().y() );

        brushes << new Brush( this );
        brushes[ brushes.size()-1 ]->setVisible(true);
        brushes[ brushes.size()-1 ]->setPos(event->pos().x(), event->pos().y());
        //brushProxyWidgets << new QGraphicsProxyWidget(0, Qt::Window);
        //brushProxyWidgets[ brushProxyWidgets.size()-1 ]->setWidget( brushes[ brushes.size()-1]);
        //brushes[ brushes.size()-1]->show();

    }
    else
    {
        mousePressStartPt = event->pos();
        clickAndDragInitialized = true;
        deltaY = 0.0;

        highlightRect.setCoords( boundingRect().x(), boundingRect().y(), boundingRect().x()+boundingRect().width(), boundingRect().y()+boundingRect().height() );

        setFocus();
    }
}

//on mouse move event
//  if user has initialized a navigation event (click and drag)
//  then compute how much to move in the contigs on screen
void BacterialContigBrowser::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if( event->modifiers() == Qt::ControlModifier )
    {
        brushes[ brushes.size()-1 ]->setWidth( event->pos().x()-rubberBandOrigin->x() );//setGeometry( QRect(rubberBandOrigin->x(), rubberBandOrigin->y(), event->pos().x(), event->pos().y()).normalized() );
        brushes[ brushes.size()-1 ]->setHeight( event->pos().y()-rubberBandOrigin->y() );
        qDebug() << "resizing rubber band to " << event->pos().x() << "," << event->pos().y() << endl;
        update();
    }
    else if( clickAndDragInitialized )
    {
        QPointF currPt = event->pos();
        deltaY = currPt.y() - mousePressStartPt.y();
        startPosOfContigs = boundingRect().y() + deltaY;
        positionContigs();

        highlightRect.setCoords( boundingRect().x(), boundingRect().y()+deltaY, boundingRect().x()+boundingRect().width(), boundingRect().y()+boundingRect().height()+deltaY );

        qDebug() << "mouse move " << deltaY << endl;
    }
}

//on mouse release event
//  end the click and drag event
//  and reset values so that elements can be displayed
void BacterialContigBrowser::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "mouse release  " << event->pos().x() << " " << event->pos().y()  ;

    if( event->modifiers() == Qt::ControlModifier )
    {
        qDebug() <<" done resizing"  << event->pos().x() << "," << event->pos().y() << endl;

    }
    else
    {
        clickAndDragInitialized = false;

        //compute new start contig and start pos for this contig
        int numContigsShifted = deltaY / defaultContigHeight ;
        startContig = startContig - numContigsShifted;

        if( startContig < 0 )
            startContig = 0;
        if( startContig >= contigs.size() )
            startContig = contigs.size()-1;

        startPosOfContigs = boundingRect().y();//startPosOfContigs + deltaY;

        highlightRect.setCoords( 0, 0, 0, 0 );

        positionContigs();
        update();
    }
}

void BacterialContigBrowser::exportSelectedGenesSlot()
{
    qDebug() << "exporting " << endl;

    //get file name for export
    QFileDialog *fileDialog = new QFileDialog;
    QString exportFileName = fileDialog->getSaveFileName(0, "Select export file name and directory");

    qDebug() << "export file name = " << exportFileName << endl;

    delete( fileDialog );

    QThread* thread = new QThread;
    Exporter* exporter = new Exporter(*fastaFileName, exportFileName, &contigs );
    exporter->moveToThread(thread);
    connect(exporter, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
    connect(thread, SIGNAL(started()), exporter, SLOT(process()));
    connect(exporter, SIGNAL(finished()), thread, SLOT(quit()));
    connect(exporter, SIGNAL(finished()), exporter, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(exporter, SIGNAL(trackProgress(int)), this, SLOT(updateProgressBar(int)));
    thread->start();

    menuBar->updateProgressMessage("Exporting");
//    delete thread; //I think no need to delete, because of 'deleteLater'
//    delete exporter;
}

void BacterialContigBrowser::sortSlot(int sortType)
{
    if( sortType == 0 )
    {
        qSort( contigs.begin(), contigs.end(), contigLengthLessThan );
        positionContigs();
        update();
    }
    if( sortType == 1 )
    {
        //get selected clusters- make sure no duplicates
        selectedClusters.clear();
        for(int i = 0; i < contigs.size(); i++)
        {
            QStringList clusters = contigs.at(i)->getSelectedClusters();
            bool contains = false;
            for(int i = 0; i < clusters.size(); i++)
            {
                QString clusterId = clusters[i];
                for(int j = 0; j < selectedClusters.size(); j++)
                {
                    if( clusterId.compare(selectedClusters[j]) == 0 )
                        contains = true;
                }
                if( !contains )
                    selectedClusters << clusterId;
                contains = false;
            }
        }

        qSort( contigs.begin(), contigs.end(), contigContainsSelectedGenesLessThan );
        positionContigs();
        update();
    }

}

void BacterialContigBrowser::updateProgressBar(int steps)
{
    if(steps == 100)
    {
        menuBar->updateProgressMessage("Done");
        menuBar->resetProgressBar();
    }
    else
        menuBar->updateProgressBar(steps);
}

void BacterialContigBrowser::advance()
{
    emit( timeout() );
}

